﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;
using sharpCmd.Controls.FileSystemViewers;
using sharpCmd.Classes.IO;

namespace sharpCmd.Controls
{
    public partial class DirectoryViewPanel : UserControl
    {
        #region ctor
        public DirectoryViewPanel()
        {
            InitializeComponent();
            ViewType = PanelViewType.Brief;
            PathsForDrives = new Dictionary<string, string>();
            FileSystemViewers = new List<FileSystemViewer>();
            FSViewersTabPages = new Dictionary<FileSystemViewer, TabPage>();
            //load combobox
            toolStripComboBoxDrives.BeginUpdate();
            toolStripComboBoxDrives.Items.Clear();
            //enumerate drives
            var drives = DrivesManager.Instance.GetDrives();
            foreach (var drive in drives)
            {
                toolStripComboBoxDrives.Items.Add(drive);
                //TODO select correct one on load
            }
            toolStripComboBoxDrives.EndUpdate();
        }
        #endregion

        #region fields+properties
        public FileSystemViewer ActiveFileSystemViewer { get; set; }
        private List<FileSystemViewer> FileSystemViewers;
        private Dictionary<FileSystemViewer, TabPage> FSViewersTabPages;

        private string path;
        public string Path
        {
            get { return path; }
            set
            {
                bool changed = path != value;
                if (changed)
                {
                    path = value;
                    InitPath();
                    if (this.PathChanged != null)
                        this.PathChanged(this, path);
                }
            }
        }

        public string Drive { get; private set; }
        private Dictionary<string, string> PathsForDrives;
        public DriveInfo DriveInfo { get; set; }
        public PanelViewType ViewType;
        #endregion

        #region consts
        const string STATUS_BAR_FORMAT_STRING = "{0:N} k / {1:N} k in {2:N} / {3:N} files, {4:N} / {5:N} dirs";
        const string TOOLSTRIP_DISK_SPACE_FORMAT_STRING = "{0:N} KiB of {1:N} KiB free"; //TODO allow different size units than KiB
        #endregion

        #region focus & enter & activation
        /// <summary>
        /// Subscribes all children to Focus events
        /// </summary>
        /// <param name="controls"></param>
        private void SubscribeChildrenEnter(ControlCollection controls)
        {
            foreach (Control c in controls)
            {
                c.Enter += new EventHandler(c_Enter);
                if (c.Controls.Count > 0)
                    SubscribeChildrenEnter(c.Controls);
            }
        }

        void c_Enter(object sender, EventArgs e)
        {
            //fire my enter
            OnEnter(e);
        }


        public void DoFocus()
        {
            if (ActiveFileSystemViewer != null)
                ActiveFileSystemViewer.DoFocus();
        }

        public bool Active
        {
            set
            {
                toolStripTop.BackColor = value ? SystemColors.Highlight : SystemColors.Control;
                toolStripTop.ForeColor = value ? SystemColors.HighlightText : SystemColors.ControlText;
            }
        }
        #endregion

        #region events
        public event Util.PathChangedDelegate PathChanged;
        #endregion

        private void InitPath()
        {
            if (path == null)
                return;
            if (tabControl.TabPages.Count == 0)
            {
                OpenNewTab();
            }
            SelectDriveInCombobox();
        }

        protected FileSystemViewer MakeDefaultFileSystemViewer()
        {
            FileSystemViewer f;
            switch (ViewType)
            {
                case PanelViewType.Brief:
                    f = new BriefFSViewer();
                    break;
                case PanelViewType.Full:
                    f = new FullFSViewer();
                    break;
                case PanelViewType.Thumbnail:
                    throw new NotImplementedException("Thumbnail not implemented");
                case PanelViewType.Tree:
                    throw new NotImplementedException("Tree not implemented");
                default: f = null;
                    break;
            }

            f.Dock = DockStyle.Fill;
            return f;
        }

        private void toolStripComboBoxDrives_DropDown(object sender, EventArgs e)
        {
            /*todo reload combobox when DrivesManager says so*/
        }

        private void toolStripComboBoxDrives_SelectedIndexChanged(object sender, EventArgs e)
        {
            string newDrive = (toolStripComboBoxDrives.SelectedItem as DrivesManager.DriveInformation).Path;
            if (newDrive != Drive)
            {
                Drive = newDrive;
            }
            //change drive
            //TODO if we had c:\temp in left pane and switched drive to C: in right, we should copy path from the other pane
            if (Path.ToLower() != Drive.ToLower())
            {
                Path = Drive;
                ActiveFileSystemViewer.Path = Path;
            }
        }

        private void SelectDriveInCombobox()
        {
            //TODO DirectoryViewPanel.SelectDriveInCombobox do this only if really the drive changes - we can detect this from old path and new path, set a flag!
            for (int i = 0; i < toolStripComboBoxDrives.Items.Count; i++)
            {
                DrivesManager.DriveInformation info = toolStripComboBoxDrives.Items[i] as DrivesManager.DriveInformation;
                if (Path.ToLower().StartsWith(info.Path.ToLower()))
                    toolStripComboBoxDrives.SelectedIndex = i;
            }
        }

        private void DirectoryViewPanel_DoubleClick(object sender, EventArgs e)
        {
            OpenNewTab();
        }

        #region tab management
        public void OpenNewTab()
        {
            FileSystemViewer f = MakeDefaultFileSystemViewer();
            OpenNewTab(f, true);
        }

        /// <summary>
        /// Opens new tab with currently selected path
        /// </summary>
        private void OpenNewTab(FileSystemViewer c, bool openInForeground)
        {
            FileSystemViewers.Add(c);
            c.Dock = DockStyle.Fill;
            c.PathChanged += new Util.PathChangedDelegate(ChildPathChanged);
            c.SelectedIndexChanged += new FileSystemViewer.SelectedIndexChangedHandler(c_SelectedIndexChanged);
            c.Path = Path;
            TabPage p = new TabPage(c.GetHeader());
            FSViewersTabPages.Add(c, p);
            p.Controls.Add(c);
            tabControl.TabPages.Add(p);
            if (openInForeground)
            {
                ActiveFileSystemViewer = c;
                ChildPathChanged(c, c.Path);
            }
        }
        #endregion

        void c_SelectedIndexChanged(int totalFileCount, int totalDirectoryCount, int selectedFileCount, int selectedDirectoryCount, long totalSize, long selectedSize)
        {
            tsslStatus.Text = string.Format(sharpCmd.Classes.FSObjectInfo.SizeFormatInfo,
                STATUS_BAR_FORMAT_STRING,
                selectedSize / 1024,
                totalSize / 1024,
                selectedFileCount,
                totalFileCount,
                selectedDirectoryCount,
                totalDirectoryCount
                );
        }

        public void ChildPathChanged(object sender, string path)
        {
            if (sender != ActiveFileSystemViewer)
                return;
            FSViewersTabPages[ActiveFileSystemViewer].Text = ActiveFileSystemViewer.GetHeader();
            //recalculate drive size
            DriveInfo i = new System.IO.DriveInfo(path);
            toolStripLabelSpace.Text = string.Format(sharpCmd.Classes.FSObjectInfo.SizeFormatInfo, TOOLSTRIP_DISK_SPACE_FORMAT_STRING, i.AvailableFreeSpace / 1024, i.TotalSize / 1024); //TODO better formatting, with thousands separator
            this.Path = path;
        }

        private void tabControl1_TabIndexChanged(object sender, EventArgs e)
        {
            //change status bar text
            PathsForDrives[Drive] = path;
            //TODO ensure drive combobox is correct on path change
        }

        private void tsbRoot_Click(object sender, EventArgs e)
        {
            ActiveFileSystemViewer.GoToRootDirectory();
        }

        private void tsbCdUp_Click(object sender, EventArgs e)
        {
            ActiveFileSystemViewer.GoToParentDirectory();
        }

        /// <summary>
        /// Reloads content of active file system viewer
        /// </summary>
        public void Reload()
        {
            ActiveFileSystemViewer.Reload();
        }

        public void DropDownCombobox()
        {
            toolStripComboBoxDrives.DroppedDown = true;
        }
    }

    public enum PanelViewType
    {
        Brief,
        Full,
        Tree,
        Thumbnail
    }
}